# coding=UTF-8
import os

from service import data_service
from utils import doc_util, json_util, k_util, date_util, read_config
from database.mapper import stock_day_macd, stock_day_k, stock_month_k, stock_month_macd


# flag:1 日线级别
def init_macd(code, flag):
    print(code + "重新生成计算macd")
    MACD_list = []
    name = data_service.stock(code)['name']
    exchange = data_service.stock(code)['exchange']
    k_result = []
    if flag == 1:
        k_result = k_util.day_k_size(code, name, exchange, 12000)
        k_result = k_util.format_k_lines(k_result)
    elif flag == 30:
        k_result = k_util.month_k_size(code, 12000)  # k_result 日期从大到小
    k_result.reverse()
    length = len(k_result)
    if length == 0:
        return MACD_list
    k0 = json_util.info_to_json(k_result[0])
    sp0 = float(k0['sp'])
    rq0 = k0['rq']
    macd_info = {'rq': rq0, 'macd': 0, 'EMA12': sp0, 'EMA26': sp0, 'DEA': 0}
    MACD_list.append(macd_info)
    for num in range(1, length):
        sp = float(json_util.info_to_json(k_result[num])['sp'])
        rq = json_util.info_to_json(k_result[num])['rq']
        macd_info = cal_macd_next(macd_info, rq, sp)
        MACD_list.append(macd_info)
    # 持久化到本地
    if flag == 1:
        file_path = os.path.join(read_config.data_path, 'common', 'macd')
        doc_util.gen_new_doc(file_path, code, MACD_list)
    elif flag == 30:
        file_path = os.path.join(read_config.data_path, 'common', 'datak', 'monthMacd')
        doc_util.gen_new_doc(file_path, code, MACD_list)
    return MACD_list


# 获取 macd
# start_rq : 开始日期 格式'2020-11-02' 倒序
# size   : 开始日期往前几天（负数往后推）
# flag   : 1.日线级别, 7.周线基本, 30月线级别
# return : 日期从大到小
def get_macd_list(code, start_rq, size, flag):
    # 获取k线
    MACD_list = []
    key = code + str(flag)  # 区分 月 周 日
    if key not in macd_cache_dict.keys():  # 没在缓存中更新缓存
        macd_list_all = []
        if flag == 1:
            macd_list_all = doc_util.get_doc_info_null(os.path.join(read_config.data_path, 'common', 'macd', code))
        elif flag == 30:
            print('code******' + code)
            if code == '':
                return MACD_list
            macd_list_all = doc_util.get_doc_info_null(
                os.path.join(read_config.data_path, 'common', 'datak', 'monthMacd', code))
        if len(macd_list_all) == 0:
            macd_list_all = init_macd(code, flag)
        set_macd_c(key, macd_list_all)
    else:
        macd_list_all = macd_cache_dict[key]
    all_len = len(macd_list_all)
    if all_len == 0: return []
    if json_util.info_to_json(macd_list_all[all_len - 1])['rq'] != date_util.find_last_valid_day():
        macd_list_all = up_macd_list(code, macd_list_all, flag)
        set_macd_c(key, macd_list_all)  # 更新缓存
    all_len = len(macd_list_all)
    for i in range(all_len):
        macd_info = json_util.to_json(macd_list_all[i])
        if date_util.date_to_timestamp(macd_info['rq']) >= date_util.date_to_timestamp(start_rq) or i == all_len - 1:
            if size > i:
                size = i + 1
            for j in range(size):
                MACD_list.append(json_util.to_json(macd_list_all[i - j]))
            break
    return MACD_list


# 全部macd更新并持久化
# flag   : 1.日线级别, 7.周线基本, 30月线级别
def up_macd_list(code, macd_list_all, flag):
    all_len = len(macd_list_all)
    last_macd_info = json_util.to_json(macd_list_all[all_len - 1])
    last_rq = last_macd_info['rq']
    if flag == 1:
        ks = k_util.day_ks_gap(code, date_util.add_day_str(last_rq, 1), date_util.get_date_str())
        for k in ks:  # 正序
            last_macd_info = cal_macd_next(last_macd_info, k['rq'], float(k['sp']))
            macd_list_all.append(last_macd_info)
    elif flag == 30:
        if last_rq[0:7] == date_util.get_date_str()[0:7]:
            ks = k_util.month_k_size(code, 1)
            for k in ks:
                macd_list_all[all_len - 1] = cal_macd_next(macd_list_all[all_len - 2], k['rq'], float(k['sp']))
        else:
            dif_month = date_util.diff_months_str(last_rq, date_util.get_date_str())
            ks = k_util.month_k_size(code, dif_month)
            ks.reverse()
            for k in ks:  # 是否有重复
                macd_list_all.append(cal_macd_next(macd_list_all[all_len - 1], k['rq'], float(k['sp'])))
    file_path = os.path.join(read_config.data_path, 'common', 'datak', 'monthMacd')
    doc_util.gen_new_doc(file_path, code, macd_list_all)
    return macd_list_all


# 根据前一天 计算下一天的macd
# macd_pre: 前一天的 macd_info
# rq : 当天的日期
# price : 当天的最新价格
def cal_macd_next(macd_pre, rq, price):
    macd_pre = json_util.to_json(macd_pre)  # 格式化
    # < 1 > 计算12日和26日移动平均线EMA1和EMA2
    # 当日EMA(12) = 前一日EMA(12)×11 / 13＋当日收盘价×2 / 13
    # 当日EMA(26) = 前一日EMA(26)×25 / 27＋当日收盘价×2 / 27
    EMA12 = macd_pre['EMA12'] + (price - macd_pre['EMA12']) * 2 / 13
    EMA26 = macd_pre['EMA26'] + (price - macd_pre['EMA26']) * 2 / 27
    # < 2 > 计算离差值(DIFF)
    # DIFF = 当日EMA(12)－当日EMA(26)
    diff = EMA12 - EMA26
    # <3>计算9日离差平均值DEA
    # 当日DEA=前一日DEA×8/10＋当日DIFF×2/10
    DEA = macd_pre['DEA'] * 0.8 + diff * 0.2
    # < 4 > 计算MACD
    # MACD = 2×(DIFF－DEA)
    MACD = 2 * (diff - DEA)
    macd_next = {'rq': rq, 'macd': MACD, 'EMA12': EMA12, 'EMA26': EMA26, 'DEA': DEA}
    return macd_next


# 缓存最近用到的
macd_cache_dict = {'000000': {"total": 0, 'del_flag': 2, 'keys1': [], 'keys2': []}}


# code = stcok_code + flag
def set_macd_c(code, data):
    cache_info = macd_cache_dict['000000']
    total = cache_info['total']
    self_check = 0
    pool_size = 10
    if total >= pool_size:
        del_flag = cache_info['del_flag']
        if del_flag == 1:
            del_list = cache_info['keys1']
            cache_info['keys1'] = []
            cache_info['del_flag'] = 2
        else:
            del_list = cache_info['keys2']
            cache_info['keys2'] = []
            cache_info['del_flag'] = 1
        for key in del_list:
            if key in macd_cache_dict.keys():
                macd_cache_dict.pop(key)
            else:
                self_check = 1
        cache_info['total'] = pool_size / 2
    if cache_info['del_flag'] == 1:
        keys2 = cache_info['keys2']
        if len(keys2) >= pool_size / 2:
            keys1 = cache_info['keys1']
            keys1.append(code)
            cache_info['del_flag'] = 2
        else:
            keys2.append(code)
        cache_info['total'] += 1
    else:
        keys1 = cache_info['keys1']
        if len(keys1) >= pool_size / 2:
            keys2 = cache_info['keys2']
            keys2.append(code)
            cache_info['del_flag'] = 1
        else:
            keys1.append(code)
        cache_info['total'] += 1
    macd_cache_dict['000000'] = cache_info
    macd_cache_dict[code] = data
    if self_check == 1:
        print("macd_cache_dict:缓存自校验")
        keys = cache_info['keys1'] + cache_info['keys2']
        self_del_list = []
        for k in macd_cache_dict.keys():
            if k != '000000' and (k not in keys):
                self_del_list.append(k)
        for k in self_del_list:
            macd_cache_dict.pop(k)


# 获取 macd 历史 含有当前价格
# start_rq : 开始日期 格式'2020-11-02' 倒序
# size   : 开始日期往前几天（负数往后推）
# flag   : 1.日线级别, 7.周线基本, 30月线级别
def get_macd_list_his(code, start_rq, size, flag):
    # 获取k线
    start_rq = date_util.find_valid_day(start_rq, -1)
    MACD_list = get_macd_list(code, start_rq, size + 1, flag)
    macd_info0 = json_util.to_json(MACD_list[0])
    macd_info1 = json_util.to_json(MACD_list[1])
    # {'rq': rq, 'macd': MACD, 'EMA12': EMA12, 'EMA26': EMA26, 'DEA': DEA}
    if start_rq == macd_info0['rq'] or start_rq == date_util.find_last_valid_day():
        return MACD_list
    else:
        k_info = k_util.month_ks_gp(code, start_rq, 1)
        MACD_list[0] = cal_macd_next(macd_info1, start_rq, float(k_info[0]['sp']))
    return MACD_list[0:size]


# 转换格式
def parse_x(data_list, key, val):
    x = []
    for data in data_list:
        x.append({'code': data['code'], 'key': data[key], 'val': data[val]})
    return x


# x 为数据
def MACD(x):
    # < 1 > 计算12日和26日移动平均线EMA1和EMA2
    # 当日EMA(12) = 前一日EMA(12)×11 / 13＋当日收盘价×2 / 13
    # 当日EMA(26) = 前一日EMA(26)×25 / 27＋当日收盘价×2 / 27
    # < 2 > 计算离差值(DIFF)
    # DIFF = 当日EMA(12)－当日EMA(26)
    # <3>计算9日离差平均值DEA
    # 当日DEA=前一日DEA×8/10＋当日DIFF×2/10
    # < 4 > 计算MACD
    # MACD = 2×(DIFF－DEA)
    if len(x) == 0:
        return []
    macd_list = [
        {'code': x[0]['code'], 'rq': x[0]['key'], 'macd': 0, 'ema12': x[0]['val'], 'ema26': x[0]['val'], 'dea': 0}]
    tmp_info = {'ema12': x[0]['val'], 'ema26': x[0]['val'], 'dea': 0}
    for i in range(1, len(x)):
        key = x[i]['key']
        val = x[i]['val']
        ema12 = (tmp_info['ema12'] * 11 + val * 2) / 13
        ema26 = (tmp_info['ema26'] * 25 + val * 2) / 27
        diff = ema12 - ema26
        dea = (tmp_info['dea'] * 8 + diff * 2) / 10
        macd = 2 * (diff - dea)
        tmp_info['ema12'] = ema12
        tmp_info['ema26'] = ema26
        tmp_info['dea'] = dea
        macd_list.append({'code': x[0]['code'], 'rq': key, 'macd': macd, 'ema12': ema12, 'ema26': ema26, 'dea': dea})
    return macd_list


# 根据前一天 计算下一天的macd
# macd_pre: 前一天的 macd_info
# rq : 当天的日期
# price : 当天的最新价格
def cal_macd_next_v1(macd_pre, rq, price):
    EMA12 = macd_pre['ema12'] + (price - macd_pre['ema12']) * 2 / 13
    EMA26 = macd_pre['ema26'] + (price - macd_pre['ema26']) * 2 / 27
    diff = EMA12 - EMA26
    DEA = macd_pre['dea'] * 0.8 + diff * 0.2
    MACD = 2 * (diff - DEA)
    macd_next = {'code': macd_pre['code'], 'rq': rq, 'macd': MACD, 'ema12': EMA12, 'ema26': EMA26, 'dea': DEA}
    return macd_next


def init_day_macd(code):
    print('初始化日macd,start,code' + code)
    day_k_list = stock_day_k.select_all_asc(code)
    x = parse_x(day_k_list, 'rq', 'sp')
    macd_list = MACD(x)
    if not macd_list or len(macd_list) == 0:
        return
    stock_day_macd.delete_all(code)
    stock_day_macd.insert_multiple(macd_list)
    print('初始化日macd,end,code' + code)

# 初始化月macd
def init_month_macd(code):
    print('初始化月macd,start,code' + code)
    month_k_list = stock_month_k.select_all(code)
    month_k_list.reverse()
    x = parse_x(month_k_list, 'rq', 'sp')
    macd_list = MACD(x)
    if not macd_list or len(macd_list) == 0:
        return
    stock_month_macd.delete_all(code)
    stock_month_macd.insert_multiple(macd_list)
    print('初始化月macd,end,code' + code)


# 根据实时值获取list
def get_macd_rt(code, size, rq, rt_price):
    macd_list = stock_day_macd.select_interval(code, rq, size)
    if len(macd_list) <= 1:
        return macd_list
    next_macd = cal_macd_next_v1(macd_list[1], rq, rt_price)
    macd_list[0]['ema12'] = next_macd['ema12']
    macd_list[0]['ema26'] = next_macd['ema26']
    macd_list[0]['dea'] = next_macd['dea']
    macd_list[0]['macd'] = next_macd['macd']
    return macd_list


if __name__ == '__main__':
    macd_list1 = get_macd_list_his('600031', '2021-03-01', 10, 30)
    for num in range(len(macd_list1)):
        print(macd_list1[num])
